interface CompressOptions {
  /** 压缩质量 0 ~ 1 */
  quality?: number;
  /** 最大像素值 大于该阈值 进行比例压缩 */
  maxPx?: number;
}

interface ImgCompress {
  (file: File, options?: CompressOptions): Promise<{
    url: string;
    file: File;
  }>;
}

interface FileToImage {
  (file: File): Promise<HTMLImageElement>;
}

const imgCompress: ImgCompress = async (origin, options) => {
  const img = await fileToImg(origin);
  const url = ImgToBase64Str(img, origin.type, options);
  const bolbEl = Base64toBlob(url);
  const file = new File([bolbEl], origin.name, { type: bolbEl.type });
  return Promise.resolve({ url, file });
};

/**
 * 图片转 base64
 * @param img
 * @param options
 */
const ImgToBase64Str = (
  img: HTMLImageElement,
  type: string,
  options?: CompressOptions
): string => {
  let canvasWidth = img.width;
  let convasHeight = img.height;
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
  /** 大于400w像素 进行尺寸比例缩放 */
  let ratio = (canvasWidth * convasHeight) / (options?.maxPx || 400000);
  let sq = 1;
  if (ratio > 1) {
    sq = Math.sqrt(ratio);
    canvasWidth /= sq;
    convasHeight /= sq;
  }
  canvas.width = canvasWidth;
  canvas.height = convasHeight;
  // 铺底色
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, canvasWidth, convasHeight);
  // 超过100W像素瓦片绘制
  let count = (canvasWidth * convasHeight) / 1000000;
  if (count > 1) {
    // 瓦片canvas
    let tCanvas = document.createElement("canvas");
    let tctx = tCanvas.getContext("2d") as CanvasRenderingContext2D;
    count = ~~(Math.sqrt(count) + 1); // 瓦片数量
    let nw = ~~(canvasWidth / count);
    let nh = ~~(convasHeight / count);
    tCanvas.width = nw;
    tCanvas.height = nh;
    for (let i = 0; i < count; i++) {
      for (let j = 0; j < count; j++) {
        tctx.drawImage(
          img,
          i * nw * sq,
          j * nh * sq,
          nw * sq,
          nh * sq,
          0,
          0,
          nw,
          nh
        );
        ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
      }
    }
  } else {
    ctx.drawImage(img, 0, 0, canvasWidth, convasHeight);
  }

  return canvas.toDataURL(type, options?.quality || 1);
};

/**
 * file转 img
 * @param file
 */
export const fileToImg: FileToImage = (file) => {
  return new Promise((resolve, reject) => {
    if (!file || !window.FileReader)
      return reject(new Error("bower not support!!"));
    if (!/^image/.test(file.type)) return reject(new Error("place use images"));
    const render = new FileReader();
    render.readAsDataURL(file);
    render.onloadend = function () {
      const result = this.result;
      const img = new Image();
      img.src = result as string;
      img.onload = () => resolve(img);
    };
  });
};
/** 将 base64 转成 Blob */
export const Base64toBlob = (base64Str: string): Blob => {
  const arr = base64Str.split(",");
  const a0 = arr[0].match(/:(.*?);/) as string[];
  const mime = a0[1];
  const arrayBuffer = Base64toArrayBuffer(base64Str);
  return new Blob([arrayBuffer], { type: mime });
};

/** base64 转 Unit8Array */
export const Base64toArrayBuffer = (base64Str: string): Uint8Array => {
  const arr = base64Str.split(",");
  const a1 = arr[1] as string;
  let bstr = atob(a1),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return u8arr;
};

export default imgCompress;
